﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace SubtitlesCleaner
{
    class Subtitle
    {
        public TimeSpan start;
        public TimeSpan end;
        public List<string> lines = new List<string>();
        bool isTimeSetValue = false;
        static int counter = 1;
        static readonly DateTimeFormatInfo dfi = new DateTimeFormatInfo();
        static readonly string Bracket1Open = "{";
        static readonly string Bracket1Close = "}";
        static readonly string Bracket2Open = "[";
        static readonly string Bracket2Close = "]";
        static readonly TimeSpan Tick = new TimeSpan(10000);
        static readonly TimeSpan OneSecond = new TimeSpan(0, 0, 1);
        static readonly TimeSpan TwoSeconds = new TimeSpan(0, 0, 2);
        static readonly TimeSpan FourSeconds = new TimeSpan(0, 0, 4);

        static Subtitle()
        {
            dfi.LongTimePattern = "HH:mm:ss,fff";
            dfi.ShortDatePattern = string.Empty;
        }

        public Subtitle()
        {
        }

        public static void ResetCounter()
        {
            counter = 1;
        }

        public Subtitle(string line, string fileName, double rate, Subtitle previous)
        {
            if (line.StartsWith(Bracket1Open) || line.StartsWith(Bracket2Open))
            {
                string bracketOpen;
                string bracketClose;

                if (line.StartsWith(Bracket1Open))
                {
                    bracketOpen = Bracket1Open;
                    bracketClose = Bracket1Close;
                }
                else
                {
                    bracketOpen = Bracket2Open;
                    bracketClose = Bracket2Close;
                }

                int i = line.IndexOf(bracketClose);
                if (i < 2)
                    SubtitleFormatException.Write(fileName, line);

                string s1 = line.Substring(1, i - 1);

                i = line.IndexOf(bracketOpen, i);
                if (i < 0)
                    SubtitleFormatException.Write(fileName, line);

                int i2 = line.IndexOf(bracketClose, i);
                if (i2 < 0)
                    SubtitleFormatException.Write(fileName, line);

                string s2 = line.Substring(i + 1, i2 - i - 1);

                if (s1.IndexOf(':') < 0)
                {
                    if(bracketOpen.Equals(Bracket2Open))
                        decodeMsTime(s1, s2);
                    else
                        decodeFrameTime(s1, s2, rate);
                }
                else
                    decodeTime(s1, s2);

                decodeLines(line.Substring(i2 + 1));
            }
            else
            {
                //TMPlayer 
                int i = line.IndexOf(':');
                if ((i == 1) || (i == 2))
                {
                    if (i == 1) //0:03:46:Nic.|Od dwudziestu minut żadnych zabudowań. 
                    {
                        decodeTime(line.Substring(0, 7), string.Empty);
                        decodeLines(line.Substring(8));
                    }
                    else //00:03:46:Nic.|Od dwudziestu minut żadnych zabudowań. 
                    {
                        decodeTime(line.Substring(0, 8), string.Empty);
                        decodeLines(line.Substring(9));
                    }
                }
                else
                    //throw new SubtitleFormatException(fileName, line);
                    SubtitleFormatException.Write(fileName, line);
            }

            correctEndTime();

            if (previous != null)
                if (start < previous.end)
                    previous.end = start.Subtract(Tick);

            previous = this;
        }

        private void correctEndTime()
        {
            if (end > start)
            {
                TimeSpan time = end.Subtract(start);

                if ((time < OneSecond) || (time > FourSeconds))
                    end = start.Add(FourSeconds);
            }
            else
                end = start.Add(FourSeconds);
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder((counter++).ToString());
            sb.AppendLine();
            sb.Append(formatTimeSpan(start));
            sb.Append(" --> ");
            sb.AppendLine(formatTimeSpan(end));

            foreach (string line in lines)
                sb.AppendLine(line);

            return sb.ToString();
        }

        private string formatTimeSpan(TimeSpan ts)
        {
            return new DateTime(ts.Ticks).ToString(dfi).Trim();
        }

        private void decodeLines(string p)
        {
            string[] ls = p.Split('|');
            foreach (string line in ls)
                if (!string.IsNullOrEmpty(line.Trim()))
                    lines.Add(cleanLine(line));
        }

        private string cleanLine(string line)
        {
            line = cutOfTag(line, "{c:");
            line = cutOfTag(line, "{y:");

            line = line
                .Replace(@"/", string.Empty)
                .Replace(@"<b>", string.Empty)
                .Replace(@"</b>", string.Empty)
                .Replace(@"<i>", string.Empty)
                .Replace(@"</i>", string.Empty)
                .Replace(@"  ", " ");

            return line;
        }

        private string cutOfTag(string line, string tag)
        {
            int i1;
            int i2;

            do
            {
                i1 = line.ToLower().IndexOf(tag);
                if (i1 > -1)
                {
                    i2 = line.IndexOf("}", i1);
                    if (i2 < i1)
                        break;
                    line = line.Remove(i1, 1 + i2 - i1);
                }
            } while (i1 > -1);
            return line;
        }

        private void decodeTime(string s1, string s2)
        {
            start = TimeSpan.Parse(s1);

            if (!string.IsNullOrEmpty(s2))
                end = TimeSpan.Parse(s2);
        }

        private void decodeMsTime(string s1, string s2)
        {
            start = TimeSpan.FromSeconds(Int32.Parse(s1) / 10);

            if (!string.IsNullOrEmpty(s2))
                end = TimeSpan.FromSeconds(Int32.Parse(s2) / 10);
        }

        private void decodeFrameTime(string s1, string s2, double rate)
        {
            start = TimeSpan.FromSeconds(Int32.Parse(s1) / rate);
            if (!string.IsNullOrEmpty(s2))
                end = TimeSpan.FromSeconds(Int32.Parse(s2) / rate);
        }

        internal void decodeSubRipTime(string line)
        {
            //00:00:07,091 --> 00:00:09,469
            line = line.Replace(',', '.');
            int i = line.IndexOf("-->");
            string s1 = line.Substring(0, i).Trim();
            string s2 = line.Substring(i + 3).Trim();

            start = TimeSpan.Parse(s1);
            end = TimeSpan.Parse(s2);

            isTimeSetValue = true;
        }

        internal bool isTimeSet()
        {
            return isTimeSetValue;
        }
    }
}

